Effective Java 2.0_中英文对照_Item 10

文章作者:Tyan
博客:noahsnail.com | CSDN | 简书

Item10: Always override toString

While java.lang.Object provides an implementation of the toString method, the string that it returns is generally not what the user of your class wants to see. It consists of the class name followed by an “at” sign (@) and the unsigned hexadecimal representation of the hash code, for example, “PhoneNumber@163b91.” The general contract for toString says that the returned string should be “a concise but informative representation that is easy for a person to read” [JavaSE6]. While it could be argued that “PhoneNumber@163b91” is concise and easy to read, it isn’t very informative when compared to “(707) 867-5309.” The toString contract goes on to say, “It is recommended that all subclasses override this method.” Good advice, indeed!

尽管java.lang.Object提供了toString方法的实现,但是通常情况下它返回的字符串不是使用类的用户想要的。返回的字符串包含类名,后面是一个@符号加上哈希码的十六进制表示,例如PhoneNumber@163b91toString的通用约定指出,返回值应该是“简洁但易读的信息表示”[JavaSE6]。虽然可以认为PhoneNumber@163b91简洁易读,但它与(707) 867-5309相比,它的信息不够丰富。toString约定进一步指出,“建议所有的子类重写这个方法”。确实是个好建议。

While it isn’t as important as obeying the equals and hashCode contracts (Item 8, Item 9), providing a good toString implementation makes your class much more pleasant to use. The toString method is automatically invoked when an object is passed to println, printf, the string concatenation operator, or assert, or printed by a debugger. (The printf method was added to the platform in release 1.5, as were related methods including String.format, which is roughly equivalent to C’s sprintf.)

虽然它不像遵守equalshashCode约定(Item 8, Item 9)那样重要,但是提供一个好的toString实现可以使你的类用起来更舒适。当对象传到printlnprintf,字符串连接操作符,或assert中,或通过调试器打印时,会自动调用toString方法。(Java 1.5版本中平台加入了printf方法,相关的方法包括String.format,类似于C语言中的sprintf方法)。

If you’ve provided a good toString method for PhoneNumber, generating a useful diagnostic message is as easy as this:

如果你已经为PhoneNumber提供了一个好的toString方法,生成有用的诊断信息是很容易的:

1
System.out.println("Failed to connect: " + phoneNumber);

Programmers will generate diagnostic messages in this fashion whether or not you override toString, but the messages won’t be useful unless you do. The benefits of providing a good toString method extend beyond instances of the class to objects containing references to these instances, especially collections. Which would you rather see when printing a map, “{Jenny=PhoneNumber@163b91}” or “{Jenny=(707) 867-5309}”?

无论你是否重写toString方法,程序员们都会以这种方式生成诊断信息,但除非你重写了toString方法,否则这些信息是无用的。提供一个好的toString方法的好处是除了类的实例之外,也扩展了包含这些实例引用的对象,尤其是集合。当打印一个映射时,{Jenny=PhoneNumber@163b91}{Jenny=(707) 867-5309}你更喜欢哪一个?

When practical, the toString method should return all of the interesting information contained in the object, as in the phone number example just shown. It is impractical if the object is large or if it contains state that is not conducive to string representation. Under these circumstances, toString should return a summary such as “Manhattan white pages (1487536 listings)” or “Thread[main,5,main]”. Ideally, the string should be self-explanatory. (The Thread example flunks this test.)

当实践时,toString方法应该返回包含在对象中的所有的感兴趣信息,正如刚才电话号码的例子展示的那样。如果对象很大或它包含不能用字符串表示的状态,重写toString方法是不切实际的。在这种情况下,toString应该返回一个概要信息,例如Manhattan white pages (1487536 listings)Thread[main,5,main]。理想情况下,字符串应该是自解释的。(Thread例子不能满足这样的要求。)

One important decision you’ll have to make when implementing a toString method is whether to specify the format of the return value in the documentation. It is recommended that you do this for value classes, such as phone numbers or matrices. The advantage of specifying the format is that it serves as a standard, unambiguous, human-readable representation of the object. This representation can be used for input and output and in persistent human-readable data objects, such as XML documents. If you specify the format, it’s usually a good idea to provide a matching static factory or constructor so programmers can easily translate back and forth between the object and its string representation. This approach is taken by many value classes in the Java platform libraries, including BigInteger, BigDecimal, and most of the boxed primitive classes.

当实现toString时,你要做的一个重要决定是是否在文档中指定返回值的格式。对于值类建议你这样做,例如电话号码或矩阵。指定返回值格式的优势在于它能为对象提供一个标准的,清晰的,可读的表示。这个表示可以用在输入输出中,也可以用在一致的可读数据对象中,例如XML文档。如果你指定了格式,提供一个匹配的静态工厂或构造函数通常是一个好主意,程序员可以很容易地在对象和它的字符串表示之间来回转换。Java平台库中许多值类都采用了这个方法,包括BigIntegerBigDecimal和大多数基本类型的包装类。

The disadvantage of specifying the format of the toString return value is that once you’ve specified it, you’re stuck with it for life, assuming your class is widely used. Programmers will write code to parse the representation, to generate it, and to embed it into persistent data. If you change the representation in a future release, you’ll break their code and data, and they will yowl. By failing to specify a format, you preserve the flexibility to add information or improve the format in a subsequent release.

指定toString返回值格式的劣势在于一旦你指定了它,假设你的类被广泛使用,你就必须一直坚持它。程序员将会写代码转换这种表示,产生这种格式并将它嵌入到持久化数据中。如果你在将来的版本中更改了表示格式,你将会破坏他们的代码和数据,他们将会抱怨。如果你没有指定格式,你保留了添加信息的灵活性或者在后续版本改进这种格式。

Whether or not you decide to specify the format, you should clearly document your intentions. If you specify the format, you should do so precisely. For example, here’s a toString method to go with the PhoneNumber class in Item 9:

无论你决定是否指定格式,你都应该清楚地表明你的意图。如果你指定了格式,你应该准确的去做。例如,下面的Item 9中PhoneNumber类的toString方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 /**
* Returns the string representation of this phone number.
* The string consists of fourteen characters whose format
* is "(XXX) YYY-ZZZZ", where XXX is the area code, YYY is
* the prefix, and ZZZZ is the line number. (Each of the
* capital letters represents a single decimal digit.)
*
* If any of the three parts of this phone number is too small
* to fill up its field, the field is padded with leading zeros.
* For example, if the value of the line number is 123, the last * four characters of the string representation will be "0123". *
* Note that there is a single space separating the closing
s* parenthesis after the area code from the first digit of the * prefix.
*/
@Override public String toString() {
return String.format("(%03d) %03d-%04d",areaCode, prefix, lineNumber);
}

If you decide not to specify a format, the documentation comment should read something like this:

如果你没有指定格式,文档注释读起来应该如下:

1
2
3
4
5
/**
* Returns a brief description of this potion. The exact details * of the representation are unspecified and subject to change, * but the following may be regarded as typical:
*
* "[Potion #9: type=love, smell=turpentine, look=india ink]" */
@Override public String toString() { ... }

After reading this comment, programmers who produce code or persistent data that depends on the details of the format will have no one but themselves to blame when the format is changed.

写代码或持久化数据的依赖于格式细节的程序员,在读了这个文档之后,一旦格式改变,只能自己负责后果。

Whether or not you specify the format, provide programmatic access to all of the information contained in the value returned by toString. For example, the PhoneNumber class should contain accessors for the area code, prefix, and line number. If you fail to do this, you force programmers who need this information to parse the string. Besides reducing performance and making unnecessary work for programmers, this process is error-prone and results in fragile systems that break if you change the format. By failing to provide accessors, you turn the string format into a de facto API, even if you’ve specified that it’s subject to change.

无论你是否指定了格式,都应该提供toString返回值中包含的所有信息的程序访问接口。例如,PhoneNumber类应该包含区域码,前缀和行号的访问器。如果你没有这样做,你会迫使需要这个信息的程序员取转换这个字符串。除了为程序员降低效率和造成不必要的工作之外,这个过程中很容易出错,而且会导致系统非常脆弱,如果你更改了格式系统会崩溃。如果没有提供访问器,即使你指明了字符串格式是可以变化的,这个字符串格式也变成了实际上的API。

如果有收获,可以请我喝杯咖啡!